package com.gitee.l0km.jsonvisitor;


import com.gitee.l0km.jsonvisitor.parser.JSONBaseVisitor;
import com.gitee.l0km.jsonvisitor.parser.JSONParser.ArrContext;
import com.gitee.l0km.jsonvisitor.parser.JSONParser.PairContext;
import com.gitee.l0km.jsonvisitor.parser.JSONParser.StringContext;
import com.gitee.l0km.jsonvisitor.parser.JSONParser.ValueContext;

import static com.gitee.l0km.jsonvisitor.utils.InterpreterUtils.unquote;

import java.util.Objects;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;


public class PairVisitor extends JSONBaseVisitor<String> {
	
	private final Predicate<String> keyFilter;
	private final Function<ValueContext,String> valueFunction;
	private final AggregateFunction<String> resultAggreate;

	public PairVisitor(String key){
		this(key, -1);
	}
	public PairVisitor(String key,int ruleIndex){
		this(new KeyFilter(key),new ValueFunction(ruleIndex),null);
	}

	public PairVisitor(Predicate<String> keyFilter, Function<ValueContext, String> valueFunction,
			AggregateFunction<String> resultAggreate) {
		this.keyFilter = keyFilter;
		this.valueFunction = valueFunction;
		this.resultAggreate = resultAggreate;
	}
	@Override
	protected String aggregateResult(String aggregate, String nextResult) {
		if(resultAggreate != null) {
			return resultAggreate.reduce(aggregate, nextResult);
		}
		if(aggregate != null) {
			return aggregate;
		}
		return nextResult;
	}
	
	protected boolean filterKey(String key) {
		if(null == keyFilter) {
			throw new IllegalStateException("keyFilter is null");
		}
		return keyFilter.test(key);
	}
	@Override
	public String visitPair(PairContext ctx) {
//		System.out.printf("%s\n", ctx.STRING().getText());
		if(filterKey(unquote(ctx.STRING().getText()))) {
			return ctx.value().accept(this);
		}
		return null;
	}

	@Override
	public String visitArr(ArrContext ctx) {
		return null;
	}

	@Override
	public String visitValue(ValueContext ctx) {
//			System.out.printf("parent:%s\n", ctx.getParent().getClass().getName());
		if (ctx.getParent() instanceof PairContext) {
			return (String) valueFunction.apply(ctx);
		}else if (ctx.obj() != null) {
			return ctx.obj().accept(this);
		}
		return null;
	}
	public static ParserRuleContext getRuleContext(ValueContext ctx,int ruleIndex) {
		if (ctx.children != null) {
			for (ParseTree o : ctx.children) {
				if (o instanceof ParserRuleContext) {
					ParserRuleContext ruleContext = (ParserRuleContext) o;
					if (ruleContext.getRuleIndex() == ruleIndex) {
						return ruleContext;
					}
				}
			}
		}
		return null;
	}
	public static class KeyFilter implements Predicate<String>{
		private final String key;
		public KeyFilter(String key) {
			this.key = key;
		}
		@Override
		public boolean test(String input) {
			return Objects.equals(key, input);
		}
		
	}

	public static class ValueFunction implements Function<ValueContext, String> {

		private final int ruleIndex;

		public ValueFunction(int ruleIndex) {
			this.ruleIndex = ruleIndex;
		}

		public ValueFunction() {
			this(-1);
		}

		@Override
		public String apply(ValueContext ctx) {
			ParserRuleContext child;
			if (ruleIndex >= 0) {
				child = getRuleContext(ctx, ruleIndex);
			} else {
				child = ctx.getRuleContext(ParserRuleContext.class, 0);
			}
			if (null != child) {
				if (child instanceof StringContext) {
					return unquote(child.getText());
				}
				return child.getText();
			}
			return null;
		}

	}
}
